Skip to content

🛡️ Sentinel: [CRITICAL] Fix command injection in pm2 API route#21

Open
bobdivx wants to merge 1 commit into
devfrom
sentinel-pm2-command-injection-6910222388751855960
Open

🛡️ Sentinel: [CRITICAL] Fix command injection in pm2 API route#21
bobdivx wants to merge 1 commit into
devfrom
sentinel-pm2-command-injection-6910222388751855960

Conversation

@bobdivx
Copy link
Copy Markdown
Owner

@bobdivx bobdivx commented May 8, 2026

🚨 Severity: CRITICAL
💡 Vulnerability: The src/pages/api/pm2.ts route concatenated user-provided values (like appName, scriptPath, and env parameters) directly into shell commands wrapped in exec. This allows trivial shell injection (e.g. ; rm -rf /; #).
🎯 Impact: An attacker with access to this endpoint could execute arbitrary commands on the host operating system with the privileges of the Node.js process.
🔧 Fix: Refactored the underlying command executions to use execFile, utilizing arrays for arguments instead of a single string for the entire execution. Passed the directory path via the cwd options parameter and passed environment variables using env parameters. Explicitly reject appName that start with - to guard against flag injections.
✅ Verification: Ran pnpm test and pnpm run check and read through .jules/sentinel.md journal file. Tested logic flow on local instance.


PR created automatically by Jules for task 6910222388751855960 started by @bobdivx

Replaced `exec` with `execFile` passing child process arguments as arrays, and validated that `appName` does not start with a hyphen to prevent flag injection, closing a shell command injection vector.

Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
forge Ready Ready Preview, Comment May 8, 2026 6:11pm

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses a command injection vulnerability in the PM2 API by replacing shell execution with execFile, validating application names to prevent flag injection, and improving environment variable handling. Feedback identifies further security enhancements, such as sanitizing sensitive environment variables like NODE_OPTIONS and validating scriptPath against path traversal. Additionally, reviewers recommended using platform-specific command names for npm and npx to ensure compatibility with Windows environments.

Comment thread src/pages/api/pm2.ts
}

// Prepare secure environment dictionary instead of string concatenation
const childEnv = { ...process.env, ...(env || {}) };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Merging user-provided environment variables directly into the child process environment is a security risk. Attackers can use variables like NODE_OPTIONS or LD_PRELOAD to execute arbitrary code or bypass security constraints. Sanitize the env object by removing sensitive keys.

Comment thread src/pages/api/pm2.ts
} else {
command = `cd ${scriptPath} && ${envString} npx -y pm2 start npm --name "${appName}" -- run ${npmScript}`;
// 🛡️ Sentinel: Using execFile with array of args to prevent shell injection
await execFilePromise('npm', ['run', 'build'], { cwd: scriptPath, env: childEnv });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The scriptPath is used as the cwd for command execution without validation, which could lead to Path Traversal vulnerabilities. Ensure scriptPath is restricted to allowed project directories. Additionally, use the platform-aware npmCmd for Windows compatibility.

Comment thread src/pages/api/pm2.ts
import fs from 'node:fs';

const execPromise = util.promisify(exec);
const execFilePromise = util.promisify(execFile);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure cross-platform compatibility (especially on Windows), it's better to define platform-specific command names for npm and npx, as execFile cannot directly execute .cmd files without them.

const execFilePromise = util.promisify(execFile);
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';

Comment thread src/pages/api/pm2.ts
} else {
return new Response(JSON.stringify({ error: 'Unknown action' }), { status: 400 });

const { stdout, stderr } = await execFilePromise('npx', ['-y', 'pm2', 'start', 'npm', '--name', appName, '--', 'run', npmScript], { cwd: scriptPath, env: childEnv });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the platform-aware npxCmd defined earlier to ensure this works on Windows systems.

Suggested change
const { stdout, stderr } = await execFilePromise('npx', ['-y', 'pm2', 'start', 'npm', '--name', appName, '--', 'run', npmScript], { cwd: scriptPath, env: childEnv });
const { stdout, stderr } = await execFilePromise(npxCmd, ['-y', 'pm2', 'start', 'npm', '--name', appName, '--', 'run', npmScript], { cwd: scriptPath, env: childEnv });

Comment thread src/pages/api/pm2.ts
else return new Response(JSON.stringify({ error: 'Unknown action' }), { status: 400 });

// 🛡️ Sentinel: Using execFile with array of args to prevent shell injection
const { stdout, stderr } = await execFilePromise('npx', ['-y', 'pm2', pm2Action, appName]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the platform-aware npxCmd defined earlier to ensure this works on Windows systems.

Suggested change
const { stdout, stderr } = await execFilePromise('npx', ['-y', 'pm2', pm2Action, appName]);
const { stdout, stderr } = await execFilePromise(npxCmd, ['-y', 'pm2', pm2Action, appName]);

Comment thread src/pages/api/pm2.ts
export const GET: APIRoute = async () => {
try {
const { stdout } = await execPromise('npx -y pm2 jlist');
const { stdout } = await execFilePromise('npx', ['-y', 'pm2', 'jlist']);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the platform-aware npxCmd defined earlier to ensure this works on Windows systems.

Suggested change
const { stdout } = await execFilePromise('npx', ['-y', 'pm2', 'jlist']);
const { stdout } = await execFilePromise(npxCmd, ['-y', 'pm2', 'jlist']);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant